package com.own.business.login.core.service.impl;

import com.own.business.login.common.property.LoginProperty;
import com.own.business.login.core.entity.vo.TokenResponseVo;
import com.own.business.login.core.service.AutoLoginService;
import com.own.business.login.core.util.LoginCommonUtil;
import com.own.component.client.base.RequestClientManager;
import com.own.component.common.exception.BusinessSimpleException;
import com.own.component.common.login.core.UserStatusHook;
import com.own.component.common.login.user.BaseLoginUser;
import com.own.component.common.login.user.DefaultLoginUser;
import com.own.component.common.login.util.SessionUserUtil;
import com.own.component.log.login.util.LogLoginUtil;
import com.own.component.store.core.Store;
import com.own.component.token.core.TokenRuleManager;
import com.own.exception.ExceptionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.UUID;

/**
 * AutoLoginServiceImpl
 *
 * @author chenxueli
 * @date 2022-11-13 11:29:00
 */
@Slf4j
@Service
public class AutoLoginServiceImpl implements AutoLoginService, UserStatusHook {

    @Resource
    private Store<String> store;

    @Resource
    private LoginCommonUtil loginCommonUtil;

    @Resource
    private LogLoginUtil logLoginUtil;

    @Resource
    private RequestClientManager requestClientManager;
    @Resource
    private LoginProperty loginProperty;

    /**
     * 生成自动登录的token
     *
     * @param userId 用户id
     * @return token
     */
    private static String generateAutoToken(Long userId) {
        var temp1 = "x" + userId;
        var temp2 = UUID.randomUUID().toString().replace("-", "");
        var temp3 = (temp1.length() - 1) + "";
        // 在temp2的第13位插入temp3和temp1
        return "v1" + temp2.substring(0, 12) + temp3 + temp1 + temp2.substring(12);
    }

    /**
     * 从自动登录的token中解析出userId
     *
     * @param token token
     * @return userId
     */
    private static Long getUserFromAutoToken(String token) {
        if (token.length() == 64 || !token.startsWith("v1")) {
            return null;
        }
        try {
            // 移除前12位
            var temp = token.substring(12);
            // 找到第一个x的位置
            var index = temp.indexOf("x");
            // 截取出userId的长度信息
            var temp0 = Integer.parseInt(temp.substring(0, index));
            var temp1 = temp.substring(index + 1);
            // 截取出userId信息
            return Long.parseLong(temp1.substring(0, temp0));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 创建自动登录密码信息
     *
     * @param client 客户端
     * @return 是否登录成功
     */
    @Override
    public String createAutoLoginKey(String client) {
        var loginUser = SessionUserUtil.getLoginUser();
        return createAutoLoginKey(loginUser.token(), client, loginUser.userId());
    }

    /**
     * 根据token和用户id创建自动登录密码信息
     *
     * @param token  token
     * @param client 客户端
     * @param userId 用户id
     * @return 是否登录成功
     */
    @Override
    public String createAutoLoginKey(String token, String client, Long userId) {
        var userIdKey = buildKey(userId, client);
        // 删除之前已有的自动登录信息（如果存在的话）
        if (store.contains(userIdKey)) {
            var password = store.value().get(userIdKey);
            store.value().remove(userIdKey);
            store.value().remove(buildPasswordKey(password));
        }
        // 生成64位的密码信息(将对应的用户id放入token中)
        var password = generateAutoToken(userId);
        var rule = TokenRuleManager.get(client);
        // 存入缓存（30天内可以自动登录）
        var duration = Duration.ofMillis(rule.getAutoLoginTimeout());
        store.value().set(buildPasswordKey(password), String.valueOf(userId), duration);
        store.value().set(userIdKey, password, duration);
        // 返回结果
        return password;
    }

    /**
     * 根据用户id删除自动登录密码信息
     *
     * @param userId 用户id
     */
    @Override
    public void deleteByUserId(Long userId) {
        // 获取当前用户登录的所有客户端
        var keys = store.value().pattern("a:l:" + userId + ":*");
        keys.forEach(item -> {
            var password = store.value().get(item);
            store.value().remove(item);
            store.value().remove(buildPasswordKey(password));
        });
    }

    /**
     * 根据用户id删除自动登录密码信息
     *
     * @param userId 用户id
     * @param client 客户端
     */
    @Override
    public void deleteByUserId(Long userId, String client) {
        var userIdKey = buildKey(userId, client);
        if (store.contains(userIdKey)) {
            var password = store.value().get(userIdKey);
            store.value().remove(userIdKey);
            store.value().remove(buildPasswordKey(password));
        }
    }

    /**
     * 自动登录
     *
     * @param password 自动登录密码
     * @param request  请求信息
     * @param response 响应信息
     * @return token
     */
    @Override
    public TokenResponseVo login(String password, HttpServletRequest request, HttpServletResponse response) {
        // 获取password中的用户id信息
        var passwordUserId = getUserFromAutoToken(password);
        // 根据密码查询用户id信息
        var passwordKey = buildPasswordKey(password);
        var client = requestClientManager.client(request);
        if (!store.contains(passwordKey)) {
            var loginUser = new DefaultLoginUser("", passwordUserId, "", 0, client);
            logLoginUtil.fail(password, ExceptionEnum.Default.AUTO_LOGIN_NOT_FOUND, loginUser);
            throw new BusinessSimpleException(ExceptionEnum.Default.AUTO_LOGIN_NOT_FOUND);
        }
        var userId = store.value().get(passwordKey);
        // 更新自动登录的缓存信息
        var duration = Duration.ofDays(30);
        store.value().set(passwordKey, userId, duration);
        store.value().set(buildKey(Long.parseLong(userId), client), password, duration);
        // 自动登录
        return loginCommonUtil.loginByUserId(Long.parseLong(userId), request, response);
    }

    /**
     * 构建key
     *
     * @param userId 用户id
     * @param client 客户端
     * @return key
     */
    private String buildKey(Long userId, String client) {
        return "a:l:" + userId + ":" + client;
    }

    /**
     * 构建key
     *
     * @param password 密码
     * @return key
     */
    private String buildPasswordKey(String password) {
        return "a:l:p:" + password;
    }

    /**
     * 清除信息
     *
     * @param loginUser 登录的用户信息
     */
    @Override
    public void clear(BaseLoginUser loginUser) {
        deleteByUserId(loginUser.userId(), loginUser.client());
    }

}
